let snd_of_fst_table list = (
	let table = Hashtbl.create (List.length list) in
	List.iter (fun (s, w) -> Hashtbl.add table s w) list;
	table
);;

let fst_of_snd_table list = (
	let table = Hashtbl.create (List.length list) in
	List.iter (fun (s, w) -> Hashtbl.add table w s) list;
	table
);;

module Reserved = struct
	
	type reserved_word =
		| AUTO
		| BREAK
		| CASE
		| CHAR
		| CONST
		| CONTINUE
		| DEFAULT
		| DO
		| DOUBLE
		| ELSE
		| ENUM
		| EXTERN
		| FLOAT
		| FOR
		| GOTO
		| IF
		| INLINE
		| INT
		| LONG
		| REGISTER
		| RESTRICT
		| RETURN
		| SHORT
		| SIGNED
		| SIZEOF
		| STATIC
		| STRUCT
		| SWITCH
		| TYPEDEF
		| UNION
		| UNSIGNED
		| VOID
		| VOLATILE
		| WHILE
		| BOOL
		| COMPLEX
		| IMAGINARY
		| BYCOPY (* Objective-C *)
		| BYREF (* Objective-C *)
		| IN (* Objective-C *)
		| INOUT (* Objective-C *)
		| ONEWAY (* Objective-C *)
		| OUT (* Objective-C *)
		| STRONG (* Objective-C *)
		| WEAK (* Objective-C *)
		| ASM (* C++ *)
		| CATCH (* C++ *)
		| CLASS (* C++ *)
		| CONST_CAST (* C++ *)
		| DELETE (* C++ *)
		| DYNAMIC_CAST (* C++ *)
		| EXPLICIT (* C++ *)
		| EXPORT (* C++ *)
		| FALSE (* C++ *)
		| FRIEND (* C++ *)
		| MUTABLE (* C++ *)
		| NAMESPACE (* C++ *)
		| NEW (* C++ *)
		| OPERATOR (* C++ *)
		| PRIVATE (* C++ *)
		| PROTECTED (* C++ *)
		| PUBLIC (* C++ *)
		| REINTERPRET_CAST (* C++ *)
		| STATIC_CAST (* C++ *)
		| TEMPLATE (* C++ *)
		| THIS (* C++ *)
		| THROW (* C++ *)
		| TRY (* C++ *)
		| TRUE (* C++ *)
		| TYPEID (* C++ *)
		| TYPENAME (* C++ *)
		| USING (* C++ *)
		| VIRTUAL (* C++ *)
		| WCHAR_T;; (* C++ *)
	
	let c_reserved_word_table = [
		"auto", AUTO;
		"break", BREAK;
		"case", CASE;
		"char", CHAR;
		"const", CONST;
		"continue", CONTINUE;
		"default", DEFAULT;
		"do", DO;
		"double", DOUBLE;
		"else", ELSE;
		"enum", ENUM;
		"extern", EXTERN;
		"float", FLOAT;
		"for", FOR;
		"goto", GOTO;
		"if", IF;
		"inline", INLINE;
		"int", INT;
		"long", LONG;
		"register", REGISTER;
		"restrict", RESTRICT;
		"return", RETURN;
		"short", SHORT;
		"signed", SIGNED;
		"sizeof", SIZEOF;
		"static", STATIC;
		"struct", STRUCT;
		"switch", SWITCH;
		"typedef", TYPEDEF;
		"union", UNION;
		"unsigned", UNSIGNED;
		"void", VOID;
		"volatile", VOLATILE;
		"while", WHILE;
		"_Bool", BOOL;
		"_Complex", COMPLEX;
		"_Imaginary", IMAGINARY];;
	
	let crw_of_string_table = snd_of_fst_table c_reserved_word_table;;
	
	let objc_reserved_word_table =
		("bycopy", BYCOPY) ::
		("byref", BYREF) ::
		("in", IN) ::
		("inout", INOUT) ::
		("oneway", ONEWAY) ::
		("out", OUT) ::
		("__strong", STRONG) ::
		("__weak", WEAK) :: c_reserved_word_table;;
	
	let objcrw_of_string_table = snd_of_fst_table objc_reserved_word_table;;
	
	let cpp_only_reserved_word_table = [
		"asm", ASM;
		"bool", BOOL;
		"catch", CATCH;
		"class", CLASS;
		"const_cast", CONST_CAST;
		"delete", DELETE;
		"dynamic_cast", DYNAMIC_CAST;
		"export", EXPORT;
		"explicit", EXPLICIT;
		"false", FALSE;
		"friend", FRIEND;
		"mutable", MUTABLE;
		"namespace", NAMESPACE;
		"new", NEW;
		"operator", OPERATOR;
		"private", PRIVATE;
		"protected", PROTECTED;
		"public", PUBLIC;
		"reinterpret_cast", REINTERPRET_CAST;
		"static_cast", STATIC_CAST;
		"template", TEMPLATE;
		"this", THIS;
		"throw", THROW;
		"true", TRUE;
		"try", TRY;
		"typeid", TYPEID;
		"typename", TYPENAME;
		"using", USING;
		"virtual", VIRTUAL;
		"wchar_t", WCHAR_T];;
	
	let cpp_reserved_word_table = cpp_only_reserved_word_table @ c_reserved_word_table;;
	
	let cpprw_of_string_table = snd_of_fst_table cpp_reserved_word_table;;
	
	let all_reserved_word_table = cpp_only_reserved_word_table @ objc_reserved_word_table;;
	
	let allrw_of_string_table = snd_of_fst_table all_reserved_word_table;;
	let string_of_allrw_table = fst_of_snd_table all_reserved_word_table;;
	
	let string_of_rw s = (
		let r = String.uppercase (Hashtbl.find string_of_allrw_table s) in
		if r.[0] = '_' then (
			let n = (if r.[1] = '_' then 2 else 1) in
			String.sub r n (String.length r - n)
		) else (
			r
		)
	);;
	
	type objc_directive =
		| CATCH
		| CLASS
		| DYNAMIC
		| ENCODE
		| END
		| FINALLY
		| IMPLEMENTATION
		| INTERFACE
		| PRIVATE
		| PROPERTY
		| PROTECTED
		| PROTOCOL
		| PUBLIC
		| SELECTOR
		| SYNCHRONIZED
		| SYNTHESIZE
		| THROW
		| TRY;;
	
	let objc_directive_table = [
		"catch", CATCH;
		"class", CLASS;
		"dynamic", DYNAMIC;
		"encode", ENCODE;
		"end", END;
		"finally", FINALLY;
		"implementation", IMPLEMENTATION;
		"interface", INTERFACE;
		"private", PRIVATE;
		"property", PROPERTY;
		"protected", PROTECTED;
		"protocol", PROTOCOL;
		"public", PUBLIC;
		"selector", SELECTOR;
		"synchronized", SYNCHRONIZED;
		"synthesize", SYNTHESIZE;
		"throw", THROW;
		"try", TRY];;
	
	let objcdir_of_string_table = snd_of_fst_table objc_directive_table;;
	let string_of_objcdir_table = fst_of_snd_table objc_directive_table;;
	
end;;

module type NumericType = sig
	type t;;
	val zero: t;;
	val scaling: base: int -> adjustment: int -> t -> t;;
	val of_basedstring: base: int -> string -> t;;
	val pp: quali:bool -> paren:bool -> Format.formatter -> t -> unit;;
end;;

module type StringType = sig
	type elm;;
	type t;;
	val empty: t;;
	val of_array: elm array -> t;;
	val pp: quali:bool -> paren:bool -> Format.formatter -> t -> unit;;
end;;

module type LexicalElementType = sig
	type integer;;
	type real;;
	type wide_string;;
	type int_prec =
		| Signed_char | Unsigned_char
		| Signed_short | Unsigned_short
		| Signed_int | Unsigned_int
		| Signed_long | Unsigned_long
		| Signed_long_long | Unsigned_long_long
	type float_prec =
		| Float
		| Double_float
		| Long_double_float
		| Decimal32 (* gcc's _Decimal32 *)
		| Decimal64 (* gcc's _Decimal64 *)
		| Decimal128 (* gcc's _Decimal128 *)
	type preprocessor_directive =
		| DEFINE of string * string list * t list
		| DEFINE_predefined of string * string list * t list
		| UNDEF of string
		| Unknown of string
	and t =
		| R of Reserved.reserved_word
		| ObjC_directive of Reserved.objc_directive
		| Preprocessor_directive of preprocessor_directive
		| Attribute of string (* gcc's __attribute__ *)
		| Extension (* gcc's __extension__ *)
		| Real (* gcc's __real__ *)
		| Imag (* gcc's __imag__ *)
		| Asm of string (* gcc's __asm__ *)
		| Int64 (* some compiler's __int64 *)
		| I of string
		| Int_literal of int_prec * integer
		| Float_literal of float_prec * real
		| Char_literal of char
		| Wchar_literal of int32
		| Chars_literal of string
		| Wchars_literal of wide_string
		| ObjC_string_literal of string (* @"..." *)
		| Invalid_literal
		| L_paren
		| R_paren
		| L_bracket (* [ or <: *)
		| R_bracket (* ] or :> *)
		| L_curly (* { or <% *)
		| R_curly (* } or %> *)
		| Period
		| Arrow (* -> *)
		| Increment (* ++ *)
		| Decrement (* -- *)
		| Ampersand
		| Asterisk
		| Plus
		| Minus
		| Tilde
		| Exclamation
		| Slash
		| Percent
		| L_shift (* << *)
		| R_shift (* >> *)
		| LT
		| GT
		| LE (* <= *)
		| GE (* >= *)
		| EQ (* == *)
		| NE (* != *)
		| Caret
		| Vertical
		| And (* && *)
		| Or (* || *)
		| Question
		| Colon
		| Semicolon
		| Varargs (* ... *)
		| Assign (* = *)
		| Mul_assign (* *= *)
		| Div_assign (* /= *)
		| Rem_assign (* %= *)
		| Add_assign (* += *)
		| Sub_assign (* -= *)
		| L_shift_assign (* <<= *)
		| R_shift_assign (* >>= *)
		| And_assign (* &= *)
		| Xor_assign (* ^= *)
		| Or_assign (* |= *)
		| Comma
		| Sharp (* # or %: *)
		| D_sharp (* ## or %:%: *)
		| D_colon (* C++ :: *)
		| Period_ref (* C++ .* *)
		| Arrow_ref (* C++ ->* *)
		| Include_begin of string
		| Include_end
		| Lexical_error of string
		| Eof;;

	val eof: t;;
	val invalid: t option;;
	val compare: t -> t -> int;;
	val pp_pattern: quali:bool -> paren:bool -> Format.formatter -> t -> unit
	val pp: quali:bool -> paren:bool -> Format.formatter -> t -> unit
	val pp_typename: quali:bool -> paren:bool -> Format.formatter -> t list -> unit
end;;

module type Modules = sig
	val element_module: string;;
	val reserved_module: string;;
end;;

module Element (Integer: NumericType) (Real: NumericType) (WideString: StringType)
	(Modules: Modules): (LexicalElementType with type integer = Integer.t and type real = Real.t and type wide_string = WideString.t) =
struct
	
	type integer = Integer.t;;
	type real = Real.t;;
	type wide_string = WideString.t;;
	
	type int_prec =
		| Signed_char | Unsigned_char
		| Signed_short | Unsigned_short
		| Signed_int | Unsigned_int
		| Signed_long | Unsigned_long
		| Signed_long_long | Unsigned_long_long
	
	let int_prec_table = [
		"Signed_char", Signed_char;
		"Unsigned_char", Unsigned_char;
		"Signed_short", Signed_short;
		"Unsigned_short", Unsigned_short;
		"Signed_int", Signed_int;
		"Unsigned_int", Unsigned_int;
		"Signed_long", Signed_long;
		"Unsigned_long", Unsigned_long;
		"Signed_long_long", Signed_long_long;
		"Unsigned_long_long", Unsigned_long_long];;
	
	let intprec_of_string_table = snd_of_fst_table int_prec_table;;
	let string_of_intprec_table = fst_of_snd_table int_prec_table;;
	
	type float_prec =
		| Float
		| Double_float
		| Long_double_float
		| Decimal32 (* gcc's _Decimal32 *)
		| Decimal64 (* gcc's _Decimal64 *)
		| Decimal128 (* gcc's _Decimal128 *)

	let float_prec_table = [
		"Float", Float;
		"Double_float", Double_float;
		"Long_double_float", Long_double_float;
		"Decimal32", Decimal32;
		"Decimal64", Decimal64;
		"Decimal128", Decimal128];;
	
	let floatprec_of_string_table = snd_of_fst_table float_prec_table;;
	let string_of_floatprec_table = fst_of_snd_table float_prec_table;;
	
	type preprocessor_directive =
		| DEFINE of string * string list * t list
		| DEFINE_predefined of string * string list * t list
		| UNDEF of string
		| Unknown of string
	and t =
		| R of Reserved.reserved_word
		| ObjC_directive of Reserved.objc_directive
		| Preprocessor_directive of preprocessor_directive
		| Attribute of string (* gcc's __attribute__ *)
		| Extension (* gcc's __extension__ *)
		| Real (* gcc's __real__ *)
		| Imag (* gcc's __imag__ *)
		| Asm of string (* gcc's __asm__ *)
		| Int64 (* some compiler's __int64 *)
		| I of string
		| Int_literal of int_prec * integer
		| Float_literal of float_prec * real
		| Char_literal of char
		| Wchar_literal of int32
		| Chars_literal of string
		| Wchars_literal of wide_string
		| ObjC_string_literal of string (* @"..." *)
		| Invalid_literal
		| L_paren
		| R_paren
		| L_bracket (* [ or <: *)
		| R_bracket (* ] or :> *)
		| L_curly (* { or <% *)
		| R_curly (* } or %> *)
		| Period
		| Arrow (* -> *)
		| Increment (* ++ *)
		| Decrement (* -- *)
		| Ampersand
		| Asterisk
		| Plus
		| Minus
		| Tilde
		| Exclamation
		| Slash
		| Percent
		| L_shift (* << *)
		| R_shift (* >> *)
		| LT
		| GT
		| LE (* <= *)
		| GE (* >= *)
		| EQ (* == *)
		| NE (* != *)
		| Caret
		| Vertical
		| And (* && *)
		| Or (* || *)
		| Question
		| Colon
		| Semicolon
		| Varargs (* ... *)
		| Assign (* = *)
		| Mul_assign (* *= *)
		| Div_assign (* /= *)
		| Rem_assign (* %= *)
		| Add_assign (* += *)
		| Sub_assign (* -= *)
		| L_shift_assign (* <<= *)
		| R_shift_assign (* >>= *)
		| And_assign (* &= *)
		| Xor_assign (* ^= *)
		| Or_assign (* |= *)
		| Comma
		| Sharp (* # or %: *)
		| D_sharp (* ## or %:%: *)
		| D_colon (* C++ :: *)
		| Period_ref (* C++ .* *)
		| Arrow_ref (* C++ ->* *)
		| Include_begin of string
		| Include_end
		| Lexical_error of string
		| Eof;;
	
	let eof = Eof;;
	let invalid = None;;
	let compare = compare;;

	let rec make_pp ~(pattern: bool) ~(quali: bool) ~(paren: bool) (f: Format.formatter) (e: t): unit = (
		Format.pp_open_box f 2;
		let has_value = Obj.is_block (Obj.repr e) in
		if paren && has_value then (
			Format.pp_print_char f '(';
			Format.pp_print_cut f ()
		);
		if quali then (
			Format.pp_print_string f Modules.element_module;
			Format.pp_print_char f '.'
		);
		begin match e with
		| R r ->
			Format.pp_print_string f "R";
			Format.pp_print_space f ();
			if quali then (
				Format.pp_print_string f Modules.reserved_module;
				Format.pp_print_char f '.'
			);
			Format.pp_print_string f (Reserved.string_of_rw r)
		| ObjC_directive d ->
			Format.pp_print_string f "ObjC_directive";
			Format.pp_print_space f ();
			if quali then (
				Format.pp_print_string f Modules.reserved_module;
				Format.pp_print_char f '.'
			);
			Format.pp_print_string f (String.uppercase (Hashtbl.find Reserved.string_of_objcdir_table d))
		| Preprocessor_directive d ->
			Format.pp_print_string f "Preprocessor_directive";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				begin match d with
				| DEFINE (name, args, tokens)
				| DEFINE_predefined (name, args, tokens) ->
					Format.pp_print_char f '(';
					Format.pp_open_box f 0;
					begin match d with
					| DEFINE _ -> Format.pp_print_string f "DEFINE"
					| _ -> Format.pp_print_string f "DEFINE_predefined"
					end;
					Format.pp_print_space f ();
					Format.pp_print_char f '\"';
					Format.pp_print_string f (String.escaped name);
					Format.pp_print_string f "\",";
					Format.pp_print_space f ();
					Format.pp_print_char f '[';
					Format.pp_open_box f 0;
					let (_: bool) = List.fold_left (fun first arg ->
						if not first then (
							Format.pp_print_char f ',';
							Format.pp_print_space f ()
						);
						Format.pp_print_char f '\"';
						Format.pp_print_string f (String.escaped arg);
						Format.pp_print_char f '\"';
						false
					) true args in
					Format.pp_close_box f ();
					Format.pp_print_string f "],";
					Format.pp_print_space f ();
					Format.pp_print_char f '[';
					Format.pp_open_box f 0;
					let (_: bool) = List.fold_left (fun first token ->
						if not first then (
							Format.pp_print_char f ',';
							Format.pp_print_space f ()
						);
						make_pp ~pattern f ~paren ~quali token;
						false
					) true tokens in
					Format.pp_close_box f ();
					Format.pp_print_string f "],";
					Format.pp_close_box f ();
					Format.pp_print_char f ')'
				| UNDEF name ->
					Format.pp_print_char f '(';
					Format.pp_open_box f 0;
					Format.pp_print_string f "UNDEF";
					Format.pp_print_space f ();
					Format.pp_print_char f '\"';
					Format.pp_print_string f (String.escaped name);
					Format.pp_print_char f '\"';
					Format.pp_close_box f ();
					Format.pp_print_char f ')'
				| Unknown s ->
					Format.pp_print_char f '(';
					Format.pp_open_box f 0;
					Format.pp_print_string f "Unknown";
					Format.pp_print_space f ();
					Format.pp_print_char f '\"';
					Format.pp_print_string f (String.escaped s);
					Format.pp_print_char f '\"';
					Format.pp_close_box f ();
					Format.pp_print_char f ')'
				end
			)
		| Attribute d ->
			Format.pp_print_string f "Attribute";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f (String.escaped d);
				Format.pp_print_char f '\"'
			)
		| Extension ->
			Format.pp_print_string f "Extension"
		| Real ->
			Format.pp_print_string f "Real"
		| Imag ->
			Format.pp_print_string f "Imag"
		| Asm a ->
			Format.pp_print_string f "Asm";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f (String.escaped a);
				Format.pp_print_char f '\"'
			)
		| Int64 ->
			Format.pp_print_string f "Int64"
		| I id ->
			Format.pp_print_string f "I";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f id;
				Format.pp_print_char f '\"'
			)
		| Int_literal (p, v) ->
			Format.pp_print_string f "Int_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '(';
				Format.pp_open_box f 0;
				if quali then (
					Format.pp_print_string f Modules.element_module;
					Format.pp_print_char f '.'
				);
				Format.pp_print_string f (Hashtbl.find string_of_intprec_table p);
				Format.pp_print_char f ',';
				Format.pp_print_space f ();
				Integer.pp f ~paren ~quali v;
				Format.pp_close_box f ();
				Format.pp_print_char f ')'
			)
		| Float_literal (p, v) ->
			Format.pp_print_string f "Float_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '(';
				Format.pp_open_box f 0;
				if quali then (
					Format.pp_print_string f Modules.element_module;
					Format.pp_print_char f '.'
				);
				Format.pp_print_string f (Hashtbl.find string_of_floatprec_table p);
				Format.pp_print_char f ',';
				Format.pp_print_space f ();
				Real.pp f ~paren ~quali v;
				Format.pp_close_box f ();
				Format.pp_print_char f ')'
			)
		| Char_literal v ->
			Format.pp_print_string f "Char_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\'';
				Format.pp_print_string f (Char.escaped v);
				Format.pp_print_char f '\''
			)
		| Wchar_literal v ->
			Format.pp_print_string f "Wchar_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.fprintf f "0x%lxl" v
			)
		| Chars_literal v ->
			Format.pp_print_string f "Chars_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f (String.escaped v);
				Format.pp_print_char f '\"'
			)
		| Wchars_literal v ->
			Format.pp_print_string f "Wchars_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				WideString.pp f ~paren:true ~quali:quali v
			)
		| ObjC_string_literal v ->
			Format.pp_print_string f "ObjC_string_literal";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f (String.escaped v);
				Format.pp_print_char f '\"'
			)
		| Invalid_literal ->
			Format.pp_print_string f "Invalid_literal"
		| L_paren ->
			Format.pp_print_string f "L_paren"
		| R_paren ->
			Format.pp_print_string f "R_paren"
		| L_bracket ->
			Format.pp_print_string f "L_bracket"
		| R_bracket ->
			Format.pp_print_string f "R_bracket"
		| L_curly ->
			Format.pp_print_string f "L_curly"
		| R_curly ->
			Format.pp_print_string f "R_curly"
		| Period ->
			Format.pp_print_string f "Period"
		| Arrow ->
			Format.pp_print_string f "Arrow"
		| Increment ->
			Format.pp_print_string f "Increment"
		| Decrement ->
			Format.pp_print_string f "Decrement"
		| Ampersand ->
			Format.pp_print_string f "Ampersand"
		| Asterisk ->
			Format.pp_print_string f "Asterisk"
		| Plus ->
			Format.pp_print_string f "Plus"
		| Minus ->
			Format.pp_print_string f "Minus"
		| Tilde ->
			Format.pp_print_string f "Tilde"
		| Exclamation ->
			Format.pp_print_string f "Exclamation"
		| Slash ->
			Format.pp_print_string f "Slash"
		| Percent ->
			Format.pp_print_string f "Percent"
		| L_shift ->
			Format.pp_print_string f "L_shift"
		| R_shift ->
			Format.pp_print_string f "R_shift"
		| LT ->
			Format.pp_print_string f "LT"
		| GT ->
			Format.pp_print_string f "GT"
		| LE ->
			Format.pp_print_string f "LE"
		| GE ->
			Format.pp_print_string f "GE"
		| EQ ->
			Format.pp_print_string f "EQ"
		| NE ->
			Format.pp_print_string f "NE"
		| Caret ->
			Format.pp_print_string f "Caret"
		| Vertical ->
			Format.pp_print_string f "Vertical"
		| And ->
			Format.pp_print_string f "And"
		| Or ->
			Format.pp_print_string f "Or"
		| Question ->
			Format.pp_print_string f "Question"
		| Colon ->
			Format.pp_print_string f "Colon"
		| Semicolon ->
			Format.pp_print_string f "Semicolon"
		| Varargs ->
			Format.pp_print_string f "Varargs"
		| Assign ->
			Format.pp_print_string f "Assign"
		| Mul_assign ->
			Format.pp_print_string f "Mul_assign"
		| Div_assign ->
			Format.pp_print_string f "Div_assign"
		| Rem_assign ->
			Format.pp_print_string f "Rem_assign"
		| Add_assign ->
			Format.pp_print_string f "Add_assign"
		| Sub_assign ->
			Format.pp_print_string f "Sub_assign"
		| L_shift_assign ->
			Format.pp_print_string f "L_shift_assign"
		| R_shift_assign ->
			Format.pp_print_string f "R_shift_assign"
		| And_assign ->
			Format.pp_print_string f "And_assign"
		| Xor_assign ->
			Format.pp_print_string f "Xor_assign"
		| Or_assign ->
			Format.pp_print_string f "Or_assign"
		| Comma ->
			Format.pp_print_string f "Comma"
		| Sharp ->
			Format.pp_print_string f "Sharp"
		| D_sharp ->
			Format.pp_print_string f "D_sharp"
		| D_colon ->
			Format.pp_print_string f "D_colon"
		| Period_ref ->
			Format.pp_print_string f "Period_ref"
		| Arrow_ref ->
			Format.pp_print_string f "Arrow_ref"
		| Include_begin filename ->
			Format.pp_print_string f "Include_begin";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f (String.escaped filename);
				Format.pp_print_char f '\"'
			)
		| Include_end ->
			Format.pp_print_string f "Include_end"
		| Lexical_error message ->
			Format.pp_print_string f "Lexical_error";
			Format.pp_print_space f ();
			if pattern then (
				Format.pp_print_char f '_'
			) else (
				Format.pp_print_char f '\"';
				Format.pp_print_string f (String.escaped message);
				Format.pp_print_char f '\"'
			)
		| Eof ->
			Format.pp_print_string f "Eof"
		end;
		if paren && has_value then (
			Format.pp_print_cut f ();
			Format.pp_print_char f ')'
		);
		Format.pp_close_box f ()
	);;

	let pp_pattern = make_pp ~pattern:true;;

	let pp = make_pp ~pattern:false;;

	let pp_typename ~(quali: bool) ~(paren: bool) (f: Format.formatter) (_: t list): unit = (
		if quali then (
			Format.pp_print_string f Modules.element_module;
			Format.pp_print_char f '.'
		);
		Format.pp_print_string f "t"
	);;

end;;
